// reference： https://github.com/bvaughn/react-error-boundary
/* eslint @typescript-eslint/explicit-member-accessibility:0 */

import * as React from 'react';

import { Component } from 'react';
import ErrorBoundaryFallback from './ErrorBoundaryFallback';

interface IProps {
  children?: React.ReactNode;
  Fallback?: React.ComponentType<any>;
  onError?: (err: Error, componentStack: string) => void;
}

interface IErrorInfo {
  componentStack: string;
}

interface IState {
  error?: Error | null;
  info?: IErrorInfo;
}

class ErrorBoundary extends Component<IProps, IState> {
  static defaultProps: IProps = {
    Fallback: ErrorBoundaryFallback,
  };

  constructor(props) {
    super(props);
    this.state = {
      error: null,
      info: {
        componentStack: '',
      },
    };
  }

  componentDidCatch(error: Error, info: IErrorInfo): void {
    const { onError } = this.props;

    if (typeof onError === 'function') {
      try {
        // can’t reproduce missing info in development environment.
        onError.call(this, error, info.componentStack);
      } catch (ignoredError) {
        // ignored error
      }
    }

    // Update state so the next render will show the fallback UI.
    this.setState({ error, info });
  }

  render() {
    const { children, Fallback } = this.props;
    const { error, info } = this.state;

    // render fallback UI if there is error
    if (error !== null && typeof Fallback === 'function') {
      return (
        <Fallback componentStack={info && info.componentStack} error={error} />
      );
    }

    return children || null;
  }
}

export default ErrorBoundary;
